// Copyright 2011 Jim Xochellis
// Licensed under the Apache License, Version 2.0 
// (http://www.apache.org/licenses/LICENSE-2.0)
 
#ifndef __PROF_VALUE_H__
#define __PROF_VALUE_H__

#include "prof_config.h"
#include "prof_events.h"

//---------------------------------------------------------------

namespace generic_profiling
{

template<typename _WT>
class prof_value
{
public:
	typedef _WT wrapped_type;

    prof_value()
    {
		dispatch_event(value_pure_creation_event);
    }

	prof_value(const wrapped_type &_copyValue)
    {
		wrapped_value_ = _copyValue;
		dispatch_event(value_copy_creation_event);
    }
	
    prof_value(const prof_value &_copyValue)
    {
        wrapped_value_ = _copyValue.wrapped_value_;
		dispatch_event(value_copy_creation_event);
    }

#if PROF_ENABLE_MOVE_SEMANTICS

	explicit prof_value(wrapped_type&& _moveValue)
    {
		wrapped_value_ = std::move(_moveValue);
		dispatch_event(value_move_creation_event);
    }
	
	prof_value(prof_value&& _moveValue)
	{
		 wrapped_value_ = std::move(_moveValue.wrapped_value_);
		 dispatch_event(value_move_creation_event);
	}

#endif //PROF_ENABLE_MOVE_SEMANTICS

	prof_value& operator=(const prof_value& _rightVal)
	{
		wrapped_value_ = _rightVal.wrapped_value_;
		dispatch_event(value_copy_assignment_event);
		return *this;
	}

	prof_value& operator=(const wrapped_type &_rightVal)
	{
		wrapped_value_ = _rightVal;
		dispatch_event(value_copy_assignment_event);
		return *this;
	}

#if PROF_ENABLE_MOVE_SEMANTICS

	prof_value& operator=(prof_value&& _rightVal)
	{
		wrapped_value_ = std::move(_rightVal.wrapped_value_);
		dispatch_event(value_move_assignment_event);
		return *this;
	}

	prof_value& operator=(wrapped_type&& _rightVal)
	{
		wrapped_value_ = std::move(_rightVal);
		dispatch_event(value_move_assignment_event);
		return *this;
	}

#endif //PROF_ENABLE_MOVE_SEMANTICS

	//cast-operator eliminates further profiling ability
	//operator wrapped_type () const { return wrapped_value_; } 
	wrapped_type GetValue() const { return wrapped_value_; }
	wrapped_type &GetRef()  { return wrapped_value_; }

	void SetValue(wrapped_type value) { wrapped_value_ = value; }

private:
    wrapped_type wrapped_value_;
};

//---------------------------------------------------------------

template<typename _WT>
inline bool operator<(const prof_value<_WT> &_leftVal, const prof_value<_WT> &_rightVal)
{
	dispatch_event(value_simple_comparison_event);
	return (_leftVal.GetValue() < _rightVal.GetValue());
}

template<typename _WT>
inline bool operator<=(const prof_value<_WT> &_leftVal, const prof_value<_WT> &_rightVal)
{
	dispatch_event(value_simple_comparison_event);
	return (_leftVal.GetValue() <= _rightVal.GetValue());
}

template<typename _WT>
inline bool operator>(const prof_value<_WT> &_leftVal, const prof_value<_WT> &_rightVal)
{
	dispatch_event(value_simple_comparison_event);
	return (_leftVal.GetValue() > _rightVal.GetValue());
}

template<typename _WT>
inline bool operator>=(const prof_value<_WT> &_leftVal, const prof_value<_WT> &_rightVal)
{
	dispatch_event(value_simple_comparison_event);
	return (_leftVal.GetValue() >= _rightVal.GetValue());
}

template<typename _WT>
inline bool operator==(const prof_value<_WT> &_leftVal, const prof_value<_WT> &_rightVal)
{
	dispatch_event(value_simple_comparison_event);
	return (_leftVal.GetValue() == _rightVal.GetValue());
}

template<typename _WT>
inline bool operator!=(const prof_value<_WT> &_leftVal, const prof_value<_WT> &_rightVal)
{
	dispatch_event(value_simple_comparison_event);
	return (_leftVal.GetValue() != _rightVal.GetValue());
}

//---------------------------------------------------------------
template<typename _WT>
inline prof_value<_WT> 
operator+(const prof_value<_WT> _leftVal, const prof_value<_WT> &_rightVal)
{
	dispatch_event(value_arithmetic_event);
	return _leftVal.GetValue() + _rightVal.GetValue();
}

template <class _WT>
inline prof_value<_WT>
operator-(const prof_value<_WT> &_leftVal, const prof_value<_WT> &_rightVal)
{
	dispatch_event(value_arithmetic_event);
	return _leftVal.GetValue() - _rightVal.GetValue();
}

} //namespace generic_profiling

namespace std
{

template <class _WT>
inline void swap(generic_profiling::prof_value<_WT> &_leftVal, generic_profiling::prof_value<_WT> &_rightVal)
{
	generic_profiling::dispatch_event(generic_profiling::value_swap_event);
	
	//This might further produce copy/move events...
	return std::swap(_leftVal.GetRef(), _rightVal.GetRef());
}

} //namespace std
#endif //__PROF_VALUE_H__
